/**
 * @author Yunfeng Bai
 *
 * 2010-11-12
 */
package geometry;

import geometry.TriangleMesh.TriangleIndex;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.media.opengl.GL2;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;

import util.Util;
import collision.BoundingBox;
import collision.BoundingSphere;

public class Box extends Drawable {
	private double size[];
	private List<Triangle> triangles = new ArrayList<Triangle>();
	private BoundingBox boundingBox;
	private BoundingSphere boundingSphere;
	private TriangleMesh mesh;
	private List<Point3d> vertices = new ArrayList<Point3d>();
	private List<TriangleIndex> triangleIndices = new ArrayList<TriangleIndex>();
	
	public Box(double length, double width, double height, Color3f color)
	{
		_color = color;
		size = new double[3];
		size[0] = length;
		size[1] = width;
		size[2] = height;
		
		// Origin is at (0, 0, 0).
		vertices.add(new Point3d(-length / 2, -width / 2, height / 2));
		vertices.add(new Point3d(length / 2, -width / 2, height / 2));
		vertices.add(new Point3d(length / 2, -width / 2, -height / 2));
		vertices.add(new Point3d(-length / 2, -width / 2, -height / 2));
		vertices.add(new Point3d(-length / 2, width / 2, height / 2));
		vertices.add(new Point3d(length / 2, width / 2, height / 2));
		vertices.add(new Point3d(length / 2, width / 2, -height / 2));
		vertices.add(new Point3d(-length / 2, width / 2, -height / 2));
		
		Point3d v3 = vertices.get(3);
		Point3d v5 = vertices.get(5);

		boundingBox = new BoundingBox(this, new Point3d(v3.x, v3.y, v3.z), new Point3d(v5.x, v5.y, v5.z));

		Point3d sphereOrigin = new Point3d();
		sphereOrigin.x = (boundingBox._minPt.x + boundingBox._maxPt.x) / 2;
		sphereOrigin.y = (boundingBox._minPt.y + boundingBox._maxPt.y) / 2;
		sphereOrigin.z = (boundingBox._minPt.z + boundingBox._maxPt.z) / 2;
		//distance to any point should be the same
		double radius = Util.distance(sphereOrigin, v3);

		boundingSphere = new BoundingSphere(this, sphereOrigin, radius);
		
		triangleIndices.add(new TriangleIndex(0, 5, 4));
		triangleIndices.add(new TriangleIndex(0, 1, 5));
		triangleIndices.add(new TriangleIndex(3, 7, 6));
		triangleIndices.add(new TriangleIndex(3, 6, 2));
		triangleIndices.add(new TriangleIndex(3, 4, 7));
		triangleIndices.add(new TriangleIndex(3, 0, 4));
		triangleIndices.add(new TriangleIndex(1, 6, 5));
		triangleIndices.add(new TriangleIndex(1, 2, 6));
		triangleIndices.add(new TriangleIndex(4, 6, 7));
		triangleIndices.add(new TriangleIndex(4, 5, 6));
		triangleIndices.add(new TriangleIndex(3, 1, 0));
		triangleIndices.add(new TriangleIndex(3, 2, 1));
		
//		subdivide(1);
		
		for (TriangleIndex index : triangleIndices)
		{
			triangles.add(new Triangle(vertices.get(index._a), vertices.get(index._b), vertices.get(index._c),
					color, index));
		}
		
		mesh = new TriangleMesh(vertices, triangleIndices, color);
	}
	
	public Box(double length, double width, double height)
	{
		this(length, width, height, new Color3f(0.1f, 0.1f, 0.7f));
	}
	
//	public double intersect(Vector4f r, Vector4f d) {
//		// Transform the ray into object space.
//		Vector4f or = (Vector4f) r.clone();
//		Vector4f od = (Vector4f) d.clone();
//		Matrix4f transformToWorldSpaceInverse = (Matrix4f) getTransformToWorldSpace().clone();
//		transformToWorldSpaceInverse.invert();
//		transformToWorldSpaceInverse.transform(or);
//		transformToWorldSpaceInverse.transform(od);
//		double mint = -1;
//		for (int i = 0; i < triangles.length; i++)
//		{
//			double t = triangles[i].intersection(or, od);
//			if (t >= 0)
//			{
//				if (mint == -1 || t < mint) mint = t;
//			}
//		}
//		return mint;
//	}
	
	public void subdivide(int times)
	{
		for (int i = 0; i < times; i++)
		{
			int triangleCount = triangleIndices.size();
			for (int tri = 0; tri < triangleCount; tri++)
			{
				int ida = triangleIndices.get(tri)._a;
				int idb = triangleIndices.get(tri)._b;
				int idc = triangleIndices.get(tri)._c;
				
				Point3d a = vertices.get(ida);
				Point3d b = vertices.get(idb);
				Point3d c = vertices.get(idc);
				
				// Add the mid point of this triangle as a new vertice.
				Point3d mid = new Point3d();
				mid.add(a, b);
				mid.add(c);
				mid.scale(1.0 / 3.0);
				vertices.add(mid);
				
				int idd = vertices.size() - 1;
				
				// Modify the current triangle.
				triangleIndices.get(tri)._c = idd;
				
				// Add two new triangles.
				triangleIndices.add(new TriangleIndex(idb, idc, idd));
				triangleIndices.add(new TriangleIndex(idc, ida, idd));
			}
		}
	}

	@Override
	public void build(GL2 gl)
	{
		_listNum = gl.glGenLists(1);
		gl.glNewList(_listNum, GL2.GL_COMPILE);
		
		for(Triangle t : triangles)
			t.draw(gl);
		
		gl.glEndList();
	}

	public BoundingBox getBoundingBox()
	{
		return boundingBox;
	}
	
	public BoundingSphere getBoundingSphere()
	{
		return boundingSphere;
	}
	
	@Override
	public TriangleMesh getTriangleMesh() {
		return mesh;
	}
}
